//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

/**
 * Initialization for access to a persistent memory allocator managed pool.
 * Persist Memory allocator
 */

#include "pmfunction.h"
#include "pminternal.h"

// #define HAVE_MMAP_ANON
// #define HAVE_MMAP_DEV_ZERO

void *pmalloc_attach(int fd, void *baseaddr, const size_t initial_size, bool fixSized)
{
    struct mdesc mtemp;
    struct mdesc *mdp;
    void *mbase;
    struct stat sbuf;

    if (fd >= 0) {
        if (fstat(fd, &sbuf) < 0) {
            return (nullptr);
        }
        else if (sbuf.st_size > 0) {
            // 若打开的文件之前被使用过了，读取文件中存储的内存池管理结构进行复用
            return (static_cast<void *>(reuse_mempool(fd)));
        }
    }

    mdp = &mtemp;
    memset(reinterpret_cast<char *>(mdp), 0, sizeof(mtemp));
    strncpy(mdp->magicwords, PMALLOC_MAGIC, PMALLOC_MAGIC_SIZE);
    mdp->headersize = sizeof(mtemp);
    mdp->version = PMALLOC_VERSION;
    mdp->morespace = __pmalloc_map_morespace;
    mdp->mappingfd = fd;
    mdp->watermarkpos = nullptr;
    mdp->limitpos = nullptr;
    mdp->mempoolbase = static_cast<char *>(baseaddr);

    mdp->fixedSize = fixSized;

    if (mdp->mappingfd < 0) {

    /**
     * void *mmap(void *addr, size_t length, int port, int flags, int fd, off_t offset);
     * 父进程将flags参数设置MAP_SHARED方式通过mmap申请一段内存。内存可以映射某个具体文件（fd），
     * 也可以不映射具体文件（fd置为-1，flag设置为MAP_ANONYMOUS）.
     */
#ifdef HAVE_MMAP_ANON
        mdp->flags |= PMALLOC_ANON;
#else
#ifdef HAVE_MMAP_DEV_ZERO
        if ((mdp->mappingfd = open("/dev/zero", O_RDWR)) < 0) {
            return (nullptr);
        }
        else {
            mdp->flags |= PMALLOC_DEVZERO;
        }
#else
        return nullptr;
#endif
#endif
    }

    // 现在, mdp->morespace 的值是 __pmalloc_map_morespace;
    mbase = mdp->morespace(mdp, sizeof(mtemp) + initial_size);
    if (mbase != nullptr) {
        memcpy(mbase, mdp, sizeof(mtemp));
        mdp = static_cast<struct mdesc *>(mbase);
        mdp->morespace(mdp, -initial_size);
    }
    else {
        if (mdp->flags & PMALLOC_DEVZERO) {
            close(mdp->mappingfd);
        }
        mdp = nullptr;
    }

    return static_cast<void *>(mdp);
}
